home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / General / WASTE 1.0a4 Distribution / WASTE Source / WASTE4.p < prev    next >
Text File  |  1994-04-19  |  31KB  |  1,197 lines

  1. unit WASTE4;
  2.  
  3. { WASTE PROJECT: }
  4. { Unit Four: Editing }
  5.  
  6. { Copyright © 1993-1994 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WASTE3;
  12.  
  13.     function WESetStyle (mode: Integer;
  14.                                     var ts: WETextStyle;
  15.                                     hWE: WEHandle): OSErr;
  16.     function WEUseStyleScrap (styleScrap: WEStyleScrapHandle;
  17.                                     hWE: WEHandle): OSErr;
  18.     function WEDelete (hWE: WEHandle): OSErr;
  19.     function WEInsert (textPtr: Ptr;
  20.                                     textLength: LongInt;
  21.                                     styleScrap: WEStyleScrapHandle;
  22.                                     hWE: WEHandle): OSErr;
  23.     procedure WEKey (key: Char;
  24.                                     modifiers: Integer;
  25.                                     hWE: WEHandle);
  26.     function WECut (hWE: WEHandle): OSErr;
  27.     function WEPaste (hWE: WEHandle): OSErr;
  28.     function _WEDeleteRange (rangeStart, rangeEnd: LongInt;
  29.                                     hWE: WEHandle): OSErr;
  30.     function _WEInsertText (offset: LongInt;
  31.                                     textPtr: Ptr;
  32.                                     textLength: LongInt;
  33.                                     hWE: WEHandle): OSErr;
  34.     function _WEApplyStyleScrap (rangeStart, rangeEnd: LongInt;
  35.                                     styleScrap: WEStyleScrapHandle;
  36.                                     hWE: WEHandle): OSErr;
  37.     function _WESetStyleRange (rangeStart, rangeEnd: LongInt;
  38.                                     mode: Integer;
  39.                                     var ts: WETextStyle;
  40.                                     hWE: WEHandle): OSErr;
  41.     function _WERedraw (rangeStart, rangeEnd: LongInt;
  42.                                     hWE: WEHandle): OSErr;
  43.  
  44. implementation
  45.  
  46.     function _WEInsertRun (runIndex: LongInt;
  47.                                     offset, styleIndex: LongInt;
  48.                                     pWE: WEPtr): OSErr;
  49.  
  50. { Insert a new element in the style run array, at the specified runIndex position. }
  51. { The new element consists of the pair <offset, styleIndex>. }
  52.  
  53.         var
  54.             element: RunArrayElement;
  55.             err: OSErr;
  56.     begin
  57.         _WEInsertRun := noErr;
  58.  
  59. { prepare the element record to be inserted in the array }
  60.         element.runStart := offset;
  61.         element.styleIndex := styleIndex;
  62.  
  63. { do the insertion }
  64.         err := _WEInsertSlot(pWE^.hRuns, @element, runIndex + 1, SizeOf(element));
  65.         if (err <> noErr) then
  66.             begin
  67.                 _WEInsertRun := err;
  68.                 Exit(_WEInsertRun);
  69.             end;
  70.  
  71. { increment style run count }
  72.         pWE^.nRuns := pWE^.nRuns + 1;
  73.  
  74. { increment the reference count field of the style table element }
  75. { referenced by the newly inserted style run }
  76.         with pWE^.hStyles^^[styleIndex] do
  77.             refCount := refCount + 1;
  78.  
  79.     end;  { _WEInsertRun }
  80.  
  81.     function _WERemoveRun (runIndex: LongInt;
  82.                                     pWE: WEPtr): OSErr;
  83.  
  84. { remove the specified element from the style run array }
  85.  
  86.         var
  87.             styleIndex: LongInt;
  88.     begin
  89.  
  90.         styleIndex := pWE^.hRuns^^[runIndex].styleIndex;
  91.  
  92. { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  93.         _WERemoveRun := _WERemoveSlot(pWE^.hRuns, runIndex, SizeOf(RunArrayElement));
  94.  
  95. { decrement style run count }
  96.         pWE^.nRuns := pWE^.nRuns - 1;
  97.  
  98. { decrement the reference count field of the style table element }
  99. { that was referenced by the style run we have just removed }
  100.         with pWE^.hStyles^^[styleIndex] do
  101.             refCount := refCount - 1;
  102.  
  103.     end;  { _WERemoveRun }
  104.  
  105.     procedure _WEChangeRun (runIndex: LongInt;
  106.                                     newStyleIndex: LongInt;
  107.                                     pWE: WEPtr);
  108.  
  109. { change the styleIndex field of the specified element of the style run array }
  110.  
  111.         var
  112.             oldStyleIndex: LongInt;
  113.     begin
  114.  
  115. { do the change }
  116.         with pWE^.hRuns^^[runIndex] do
  117.             begin
  118.                 oldStyleIndex := styleIndex;
  119.                 styleIndex := newStyleIndex;
  120.             end;
  121.  
  122. { decrement the reference count field of the old style table element }
  123.         with pWE^.hStyles^^[oldStyleIndex] do
  124.             refCount := refCount - 1;
  125.  
  126. { increment the reference count field of the new style table element }
  127.         with pWE^.hStyles^^[newStyleIndex] do
  128.             refCount := refCount + 1;
  129.  
  130.     end;  { _WEChangeRun }
  131.  
  132.     function _WENewStyle (var ts: WERunAttributes;
  133.                                     var styleIndex: LongInt;
  134.                                     pWE: WEPtr): OSErr;
  135.  
  136. { given the specified WERunAttributes record, find the corresponding entry }
  137. { in the style table (create a new entry if necessary), and return its index }
  138.  
  139.         var
  140.             pTable: StyleTablePtr;
  141.             element: StyleTableElement;
  142.             index, unusedIndex: LongInt;
  143.             err: OSErr;
  144.     begin
  145.         _WENewStyle := noErr;
  146.         pTable := pWE^.hStyles^;
  147.  
  148. { see if the given style already exists in the style table }
  149. { while scanning the table, also remember the position of the first unused style, if any }
  150.         index := 0;
  151.         unusedIndex := -1;
  152.         while (index < pWE^.nStyles) do
  153.             begin
  154.  
  155. { perform a bitwise comparison between the current element and the specified style }
  156.                 if _WEBlockCmp(@pTable^[index].info, @ts, SizeOf(ts)) then
  157.                     begin
  158.                         styleIndex := index;        { found: style already present }
  159.                         Exit(_WENewStyle);
  160.                     end;
  161.  
  162. { check for entries which aren't referenced and can be recycled }
  163.                 if (pTable^[index].refCount = 0) then
  164.                     unusedIndex := index;
  165.  
  166.                 index := index + 1;
  167.             end;  { while }
  168.  
  169. { the specified style doesn't exist in the style table }
  170. { see if we can recycle an unused entry }
  171.         if (unusedIndex >= 0) then
  172.             begin
  173.                 index := unusedIndex;
  174.                 pTable^[index].info := ts;
  175.             end
  176.         else
  177.             begin
  178.  
  179. { no reusable entry: we have to append a new element to the table }
  180.                 element.refCount := 0;
  181.                 element.info := ts;
  182.                 err := _WEInsertSlot(pWE^.hStyles, @element, index, SizeOf(element));
  183.                 if (err <> noErr) then
  184.                     begin
  185.                         _WENewStyle := err;
  186.                         Exit(_WENewStyle);
  187.                     end;
  188.  
  189. { update style count in the WE record }
  190.                 pWE^.nStyles := index + 1;
  191.  
  192.             end;
  193.  
  194. { return the index to the new element }
  195.         styleIndex := index;
  196.  
  197.     end;  { _WENewStyle }
  198.  
  199.     function _WERedraw (rangeStart, rangeEnd: LongInt;
  200.                                     hWE: WEHandle): OSErr;
  201.  
  202. { the WE record is guaranteed to be already locked }
  203.  
  204.         label
  205.             1;
  206.         var
  207.             pWE: WEPtr;
  208.             pLines: LineArrayPtr;
  209.             startLine, endLine: LongInt;
  210.             oldTextHeight, newTextHeight: LongInt;
  211.             r: LongRect;
  212.             viewRect, updateRect: Rect;
  213.             saveClip: RgnHandle;
  214.             savePort: GrafPtr;
  215.             err: OSErr;
  216.     begin
  217.         pWE := hWE^;
  218.  
  219. { do nothing if recalculation has been inhibited }
  220.         if (not BTST(pWE^.flags, weFInhibitRecal)) then
  221.             begin
  222.  
  223. { hide the caret }
  224.                 if BTST(pWE^.flags, weFCaretVisible) then
  225.                     _WEDrawCaret(hWE);
  226.  
  227. { remember total text height }
  228.                 oldTextHeight := pWE^.destRect.bottom - pWE^.destRect.top;
  229.  
  230. { find line range affected by modification }
  231.                 startLine := _WEOffsetToLine(rangeStart, hWE);
  232.                 endLine := _WEOffsetToLine(rangeEnd, hWE);
  233.  
  234. { recalculate line breaks starting from startLine }
  235.                 err := _WERecalBreaks(startLine, endLine, hWE);
  236.                 if (err <> noErr) then
  237.                     goto 1;
  238.  
  239. { recalculate slops }
  240.                 _WERecalSlops(startLine, endLine, hWE);
  241.  
  242. { calculate new total text height }
  243.                 newTextHeight := pWE^.destRect.bottom - pWE^.destRect.top;
  244.  
  245. { calculate the rectangle to redraw (in long coordinates) }
  246.                 r.left := -maxint;
  247.                 r.right := maxint;
  248.                 pLines := pWE^.hLines^;
  249.                 r.top := pLines^[startLine].lineOrigin;
  250.  
  251. { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  252. { otherwise we must redraw all lines from startLine on }
  253.                 if ((newTextHeight = oldTextHeight) and (endLine < pWE^.nLines - 1)) then
  254.                     r.bottom := pLines^[endLine + 1].lineOrigin
  255.                 else if (newTextHeight < oldTextHeight) then
  256.                     r.bottom := oldTextHeight
  257.                 else
  258.                     r.bottom := newTextHeight;
  259.  
  260.                 WEOffsetLongRect(r, 0, pWE^.destRect.top);
  261.  
  262. { calculate the intersection between this rectangle and the view rectangle }
  263.                 WELongRectToRect(r, updateRect);
  264.                 WELongRectToRect(pWE^.viewRect, viewRect);
  265.  
  266.                 if SectRect(updateRect, viewRect, updateRect) then
  267.                     begin
  268.  
  269. { set up the port and the clip region }
  270.                         GetPort(savePort);
  271.                         SetPort(pWE^.port);
  272.  
  273. { set the clip region to updateRect }
  274.                         saveClip := NewRgn;
  275.                         GetClip(saveClip);
  276.                         ClipRect(updateRect);
  277.  
  278. { we only really need to redraw the visible lines }
  279.                         startLine := _WEPixelToLine(updateRect.top - pWE^.destRect.top, hWE);
  280.                         endLine := _WEPixelToLine(updateRect.bottom - pWE^.destRect.top - 1, hWE);
  281.  
  282. { redraw the lines (pass TRUE in the doErase parameter) }
  283.                         _WEDrawLines(startLine, endLine, true, hWE);
  284.  
  285. { erase the portion of the update rectangle below the last line (if any) }
  286.                         pLines := pWE^.hLines^;
  287.                         updateRect.top := pWE^.destRect.top + pLines^[endLine + 1].lineOrigin;
  288.                         if (updateRect.top < updateRect.bottom) then
  289.                             EraseRect(updateRect);
  290.  
  291. { restore the clip region }
  292.                         SetClip(saveClip);
  293.                         DisposeRgn(saveClip);
  294.  
  295. { restore the port }
  296.                         SetPort(savePort);
  297.  
  298. { redraw the caret or the selection range }
  299.                         if (pWE^.selStart < pWE^.selEnd) then
  300.                             _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE)
  301.                         else
  302.                             _WEDrawCaret(hWE);
  303.  
  304.                     end;  { if SectRect }
  305.  
  306. { scroll the selection range into view }
  307.                 WESelView(hWE);
  308.  
  309.             end;  { if recal not inhibited }
  310.  
  311. { clear result code }
  312.         err := noErr;
  313.  
  314. 1:
  315. { return result code }
  316.         _WERedraw := err;
  317.  
  318.     end;  { _WERedraw }
  319.  
  320.     function _WESetStyleRange (rangeStart, rangeEnd: LongInt;
  321.                                     mode: Integer;
  322.                                     var ts: WETextStyle;
  323.                                     hWE: WEHandle): OSErr;
  324.  
  325. { alter the style attributes of the specified text range according to ts and mode }
  326. { the WE record is guaranteed to be already locked }
  327.  
  328.         label
  329.             1;
  330.         var
  331.             pWE: WEPtr;
  332.             hRuns: RunArrayHandle;
  333.             offset: LongInt;
  334.             runIndex: LongInt;
  335.             oldStyleIndex, newStyleIndex: LongInt;
  336.             runInfo: WERunInfo;
  337.             temp: Integer;
  338.             continuousStyles: SignedByte;
  339.             err: OSErr;
  340.     begin
  341.         pWE := hWE^;
  342.         hRuns := pWE^.hRuns;
  343.  
  344. { if mode contains weDoToggleFace, we need to determine which QuickDraw styles }
  345. { are continuous over the specified text range: those styles must be turned off }
  346.         if BTST(mode, kModeToggleFace) then
  347.             begin
  348.                 temp := weDoFace;
  349.                 _WEContinuousStyleRange(rangeStart, rangeEnd, temp, runInfo.runAttrs.runStyle, hWE);
  350.                 continuousStyles := runInfo.runAttrs.tsFace;
  351.             end
  352.         else
  353.             continuousStyles := 0;
  354.  
  355. { find the index to the first style run in the specified range }
  356.         offset := rangeStart;
  357.         runIndex := _WEOffsetToRun(offset, hWE);
  358.  
  359. { run thru all the style runs that encompass the selection range }
  360.         repeat
  361.  
  362. { find style index for this run and retrieve corresponding style attributes }
  363.             oldStyleIndex := hRuns^^[runIndex].styleIndex;
  364.             _WEGetIndStyle(runIndex, runInfo, hWE);
  365.  
  366. { _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run: }
  367. { correct this anomaly (which is useful for other purposes, anyway) }
  368.             if (runInfo.runEnd > pWE^.textLength) then
  369.                 runInfo.runEnd := pWE^.textLength;
  370.  
  371. { apply changes to existing style attributes as requested }
  372.             _WECopyStyle(ts, runInfo.runAttrs.runStyle, continuousStyles, mode);
  373.  
  374. { recalculate font metrics, if necessary }
  375.             if (BitAnd(mode, weDoFont + weDoSize + weDoFace + weDoAddSize) <> 0) then
  376.                 _WEFillFontInfo(pWE^.port, runInfo.runAttrs);
  377.  
  378. { get a style index for the new attributes }
  379.             err := _WENewStyle(runInfo.runAttrs, newStyleIndex, pWE);
  380.             if (err <> noErr) then
  381.                 goto 1;
  382.  
  383. { if offset falls on a style boundary and this style run has become identical }
  384. { to the previous one, merge the two runs together }
  385.             if (offset = runInfo.runStart) & (runIndex > 0) & (hRuns^^[runIndex - 1].styleIndex = newStyleIndex) then
  386.                 begin
  387.                     err := _WERemoveRun(runIndex, pWE);
  388.                     if (err <> noErr) then
  389.                         goto 1;
  390.                     runIndex := runIndex - 1;
  391.                 end;
  392.  
  393. { style index changed? }
  394.             if (oldStyleIndex <> newStyleIndex) then
  395.                 begin
  396.  
  397. { if offset is in the middle of a style run, insert a new style run in the run array }
  398.                     if (offset > runInfo.runStart) then
  399.                         begin
  400.                             err := _WEInsertRun(runIndex, offset, newStyleIndex, pWE);
  401.                             if (err <> noErr) then
  402.                                 goto 1;
  403.                             runIndex := runIndex + 1;
  404.                         end
  405.                     else
  406.  
  407. { otherwise just change the styleIndex field of the current style run element }
  408.                         _WEChangeRun(runIndex, newStyleIndex, pWE);
  409.  
  410. { if specified range ends in the middle of a style run, insert yet another element }
  411.                     if (rangeEnd < runInfo.runEnd) then
  412.                         begin
  413.                             err := _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE);
  414.                             if (err <> noErr) then
  415.                                 goto 1;
  416.                         end;
  417.  
  418.                 end;  { if oldStyle <> newStyle }
  419.  
  420. { go to next style run }
  421.             runIndex := runIndex + 1;
  422.             offset := runInfo.runEnd;
  423.  
  424.         until (offset >= rangeEnd);
  425.  
  426. { if the last style run ends exactly at the end of the specified range, }
  427. { see if we can merge it with the following style run }
  428.         if ((offset = rangeEnd) & (runIndex < pWE^.nRuns) & (hRuns^^[runIndex].styleIndex = newStyleIndex)) then
  429.             begin
  430.                 err := _WERemoveRun(runIndex, pWE);
  431.                 if (err <> noErr) then
  432.                     goto 1;
  433.             end;
  434.  
  435. { clear result code }
  436.         err := noErr;
  437.  
  438. 1:
  439. { return result code }
  440.         _WESetStyleRange := err;
  441.  
  442.     end;  { _WESetStyleRange }
  443.  
  444.     function _WEApplyStyleScrap (rangeStart, rangeEnd: LongInt;
  445.                                     styleScrap: WEStyleScrapHandle;
  446.                                     hWE: WEHandle): OSErr;
  447.  
  448. { apply the given style scrap to the specified text range }
  449. { the WE record is guaranteed to be already locked }
  450.  
  451.         var
  452.             pWE: WEPtr;
  453.             pElement: WEStyleScrapPeek;
  454.             runStart, runEnd: LongInt;
  455.             index, lastElement: Integer;
  456.             ts: WETextStyle;
  457.             err: OSErr;
  458.     begin
  459.         _WEApplyStyleScrap := noErr;
  460.         pWE := hWE^;
  461.  
  462. { loop through each element of the style scrap }
  463.         lastElement := styleScrap^^.scrpNStyles - 1;
  464.         for index := 0 to lastElement do
  465.             begin
  466.  
  467. { get a pointer to the current scrap element }
  468.                 pElement := @styleScrap^^.scrpStyleTab[index];
  469.  
  470. { calculate text run to which this element is to be applied }
  471.                 runStart := rangeStart + pElement^.first.scrpStartChar;
  472.                 if (index < lastElement) then
  473.                     runEnd := rangeStart + pElement^.second.scrpStartChar
  474.                 else
  475.                     runEnd := rangeEnd;
  476.  
  477. { perform some range checking }
  478.                 if (runEnd > rangeEnd) then
  479.                     runEnd := rangeEnd;
  480.                 if (runStart >= runEnd) then
  481.                     Cycle;
  482.  
  483. { copy style to a local variable in case memory moves }
  484.                 ts := pElement^.first.scrpAttrs.runStyle;
  485.  
  486. { apply the specified style to the range }
  487.                 err := _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, ts, hWE);
  488.                 if (err <> noErr) then
  489.                     begin
  490.                         _WEApplyStyleScrap := err;
  491.                         Exit(_WEApplyStyleScrap);
  492.                     end;
  493.  
  494.             end;  { for }
  495.     end;  { _WEApplyStyleScrap }
  496.  
  497.     procedure _WEBumpRunStart (runIndex: LongInt;
  498.                                     deltaRunStart: LongInt;
  499.                                     pWE: WEPtr);
  500.  
  501. { add deltaLineStart to the lineStart field of all line records }
  502. { starting from lineIndex }
  503.  
  504.         var
  505.             pStart: LongIntPtr;
  506.             nRuns: LongInt;
  507.     begin
  508.         pStart := @pWE^.hRuns^^[runIndex].runStart;
  509.         nRuns := pWE^.nRuns;
  510.  
  511. { loop through the style run array adjusting the runStart fields }
  512.         while (runIndex <= nRuns) do
  513.             begin
  514.                 pStart^ := pStart^ + deltaRunStart;
  515.                 pStart := LongIntPtr(LongInt(pStart) + SizeOf(RunArrayElement));
  516.                 runIndex := runIndex + 1;
  517.             end;
  518.     end;  { _WEBumpRunStart }
  519.  
  520.     function _WERemoveRunRange (rangeStart, rangeEnd: LongInt;
  521.                                     hWE: WEHandle): OSErr;
  522.  
  523. { the range of text between rangeStart and rangeEnd is being deleted }
  524. { update the style run array (and the style table) accordingly }
  525. { the WE handle must be locked on entry }
  526.  
  527.         label
  528.             1;
  529.         var
  530.             pWE: WEPtr;
  531.             pRuns: RunArrayPeek;
  532.             startRun, endRun: LongInt;
  533.             err: OSErr;
  534.     begin
  535.         pWE := hWE^;
  536.  
  537. { find the index to the first and last style runs in the specified range }
  538.         startRun := _WEOffsetToRun(rangeStart, hWE);
  539.         endRun := _WEOffsetToRun(rangeEnd, hWE) - 1;
  540.  
  541. { remove all style runs between startRun and endRun }
  542.         while (endRun > startRun) do
  543.             begin
  544.                 err := _WERemoveRun(endRun, pWE);
  545.                 if (err <> noErr) then
  546.                     goto 1;
  547.                 endRun := endRun - 1;
  548.             end;
  549.  
  550. { move back all subsequent style runs }
  551.         _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  552.  
  553.         if (endRun = startRun) and (endRun < pWE^.nRuns - 1) then
  554.             begin
  555.                 pRuns := @pWE^.hRuns^^[endRun];
  556.                 pRuns^.second.runStart := rangeStart;
  557.             end;
  558.  
  559. { remove the first style run if is has become zero length }
  560.         pRuns := @pWE^.hRuns^^[startRun];
  561.         if (pRuns^.first.runStart = pRuns^.second.runStart) then
  562.             begin
  563.                 err := _WERemoveRun(startRun, pWE);
  564.                 if (err <> noErr) then
  565.                     goto 1;
  566.                 startRun := startRun - 1;
  567.             end;
  568.  
  569. { merge the first and last runs if they have the same style index }
  570.         if (startRun >= 0) then
  571.             begin
  572.                 pRuns := @pWE^.hRuns^^[startRun];
  573.                 if (pRuns^.first.styleIndex = pRuns^.second.styleIndex) then
  574.                     begin
  575.                         err := _WERemoveRun(startRun + 1, pWE);
  576.                         if (err <> noErr) then
  577.                             goto 1;
  578.                     end;
  579.             end;
  580.  
  581. { clear result code }
  582.         err := noErr;
  583.  
  584. 1:
  585. { return result code }
  586.         _WERemoveRunRange := err;
  587.  
  588.     end;  { _WERemoveRunRange }
  589.  
  590.     procedure _WEBumpLineStart (lineIndex: LongInt;
  591.                                     deltaLineStart: LongInt;
  592.                                     pWE: WEPtr);
  593.  
  594. { add deltaLineStart to the lineStart field of all line records }
  595. { starting from lineIndex }
  596.  
  597.         var
  598.             pStart: LongIntPtr;
  599.             nLines: LongInt;
  600.     begin
  601.         pStart := @pWE^.hLines^^[lineIndex].lineStart;
  602.         nLines := pWE^.nLines;
  603.  
  604. { loop through the line array adjusting the lineStart fields }
  605.         while (lineIndex <= nLines) do
  606.             begin
  607.                 pStart^ := pStart^ + deltaLineStart;
  608.                 pStart := LongIntPtr(LongInt(pStart) + SizeOf(LineRec));
  609.                 lineIndex := lineIndex + 1;
  610.             end;
  611.     end;  { _WEBumpLineStart }
  612.  
  613.     function _WERemoveLineRange (rangeStart, rangeEnd: LongInt;
  614.                                     hWE: WEHandle): OSErr;
  615.  
  616. { the range of text between rangeStart and rangeEnd is being deleted }
  617. { update the line array accordingly }
  618. { the WE handle must be locked on entry }
  619.  
  620.         var
  621.             pWE: WEPtr;
  622.             startLine, endLine: LongInt;
  623.             err: OSErr;
  624.     begin
  625.         _WERemoveLineRange := noErr;
  626.         pWE := hWE^;
  627.  
  628. { remove all line records between rangeStart and rangeEnd }
  629.         startLine := _WEOffsetToLine(rangeStart, hWE) + 1;
  630.         endLine := _WEOffsetToLine(rangeEnd, hWE);
  631.         while (endLine >= startLine) do
  632.             begin
  633.                 err := _WERemoveLine(endLine, pWE);
  634.                 if (err <> noErr) then
  635.                     begin
  636.                         _WERemoveLineRange := err;
  637.                         Exit(_WERemoveLineRange);
  638.                     end;
  639.                 endLine := endLine - 1;
  640.             end;  { while }
  641.  
  642. { update the lineStart field of all the line records that follow }
  643.         _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  644.  
  645.     end;  { _WERemoveLineRange }
  646.  
  647.     function _WEDeleteRange (rangeStart, rangeEnd: LongInt;
  648.                                     hWE: WEHandle): OSErr;
  649.  
  650. { used internally to delete a text range }
  651. { if saveNullStyle is TRUE, the first style in the range is saved in nullStyle }
  652. { the WE record is guaranteed to be already locked }
  653.  
  654.         label
  655.             0, 1;
  656.         var
  657.             pWE: WEPtr;
  658.             runInfo: WERunInfo;
  659.             oldTextLength, newTextLength: LongInt;
  660.             pText: LongInt;
  661.             err: OSErr;
  662.     begin
  663.         pWE := hWE^;
  664.  
  665. { do nothing if the specified range is empty }
  666.         if (rangeStart = rangeEnd) then
  667.             goto 0;
  668.  
  669. { save the first style in the specified range in nullStyle }
  670.         WEGetRunInfo(rangeStart, runInfo, hWE);
  671.         pWE^.nullStyle := runInfo.runAttrs;
  672.         BSET(pWE^.flags, weFUseNullStyle);
  673.  
  674. { remove all line records between rangeStart and rangeEnd }
  675.         err := _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  676.         if (err <> noErr) then
  677.             goto 1;
  678.  
  679. { remove all style runs between rangeStart and rangeEnd }
  680.         err := _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  681.         if (err <> noErr) then
  682.             goto 1;
  683.  
  684. { calculate old and new text length }
  685.         oldTextLength := pWE^.textLength;
  686.         newTextLength := oldTextLength - (rangeEnd - rangeStart);
  687.  
  688. { move the end of the text backwards over the old selection range }
  689.         pText := LongInt(pWE^.hText^);
  690.         %_BlockMoveData(Ptr(pText + rangeEnd), Ptr(pText + rangeStart), oldTextLength - rangeEnd);
  691.  
  692. { compact the text handle }
  693.         err := %_SetHandleSize(pWE^.hText, newTextLength);
  694.         if (err <> noErr) then
  695.             goto 1;
  696.  
  697. { update textLength field }
  698.         pWE^.textLength := newTextLength;
  699.  
  700. 0:
  701. { clear result code }
  702.         err := noErr;
  703.  
  704. 1:
  705. { return result code }
  706.         _WEDeleteRange := err;
  707.  
  708.     end;  { _WEDeleteRange }
  709.  
  710.     function _WEInsertText (offset: LongInt;
  711.                                     textPtr: Ptr;
  712.                                     textLength: LongInt;
  713.                                     hWE: WEHandle): OSErr;
  714.  
  715. { this routine assumes that the WE record is already locked }
  716.  
  717.         label
  718.             0, 1;
  719.         var
  720.             pWE: WEPtr;
  721.             oldTextLength, newTextLength: LongInt;
  722.             pInsPoint: LongInt;
  723.             err: OSErr;
  724.     begin
  725.         pWE := hWE^;
  726.  
  727. { do nothing if textLength is zero or negative }
  728.         if (textLength <= 0) then
  729.             goto 0;
  730.  
  731. { calculate old and new length of text handle }
  732.         oldTextLength := pWE^.textLength;
  733.         newTextLength := oldTextLength + textLength;
  734.  
  735. { lengthen the raw text handle }
  736.         err := %_SetHandleSize(pWE^.hText, newTextLength);
  737.         if (err <> noErr) then
  738.             goto 1;
  739.  
  740. { calculate ptr to insertion point }
  741.         pInsPoint := LongInt(pWE^.hText^) + offset;
  742.  
  743. { make room for the new text }
  744.         %_BlockMoveData(Ptr(pInsPoint), Ptr(pInsPoint + textLength), oldTextLength - offset);
  745.  
  746. { insert new text at the insertion point }
  747.         %_BlockMoveData(textPtr, Ptr(pInsPoint), textLength);
  748.  
  749. { update the lineStart fields of all lines following the insertion point }
  750.         _WEBumpLineStart(_WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  751.  
  752. { update the runStart fields of all style runs following the insertion point }
  753.         _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  754.  
  755. { update various fields in the WE record }
  756.         pWE^.textLength := newTextLength;
  757.  
  758. { if there is a valid null style, apply it to the newly inserted text }
  759.         if BTST(pWE^.flags, weFUseNullStyle) then
  760.             begin
  761.                 err := _WESetStyleRange(offset, offset + textLength, weDoAll + weDoReplaceFace, pWE^.nullStyle.runStyle, hWE);
  762.                 if (err <> noErr) then
  763.                     goto 1;
  764.             end;
  765.  
  766. 0:
  767. { clear result code }
  768.         err := noErr;
  769.  
  770. 1:
  771. { return result code }
  772.         _WEInsertText := err;
  773.  
  774.     end;  { _WEInsertText }
  775.  
  776.     function WEDelete (hWE: WEHandle): OSErr;
  777.         var
  778.             pWE: WEPtr;
  779.             offset: LongInt;
  780.             runInfo: WERunInfo;
  781.             saveWELock: Boolean;
  782.             err: OSErr;
  783.     begin
  784.         WEDelete := noErr;
  785.  
  786. { lock the WE record }
  787.         saveWELock := _WESetHandleLock(hWE, true);
  788.         pWE := hWE^;
  789.         offset := pWE^.selStart;
  790.  
  791. { do nothing if the selection range is empty }
  792.         if (offset < pWE^.selEnd) then
  793.             begin
  794.  
  795. { delete the selection range }
  796.                 err := _WEDeleteRange(offset, pWE^.selEnd, hWE);
  797.                 if (err <> noErr) then
  798.                     begin
  799.                         WEDelete := err;
  800.                         Exit(WEDelete);
  801.                     end;
  802.  
  803. { selEnd becomes equal to selStart }
  804.                 pWE^.selEnd := offset;
  805.  
  806. { redraw the text }
  807.                 err := _WERedraw(offset, offset, hWE);
  808.                 if (err <> noErr) then
  809.                     begin
  810.                         WEDelete := err;
  811.                         Exit(WEDelete);
  812.                     end;
  813.  
  814.             end;  { if non-empty selection }
  815.  
  816. { unlock the WE record }
  817.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  818.  
  819.     end;  { WEDelete }
  820.  
  821.     function WEInsert (textPtr: Ptr;
  822.                                     textLength: LongInt;
  823.                                     styleScrap: WEStyleScrapHandle;
  824.                                     hWE: WEHandle): OSErr;
  825.         label
  826.             1;
  827.         var
  828.             pWE: WEPtr;
  829.             offset, endOffset: LongInt;
  830.             saveWELock: Boolean;
  831.             err: OSErr;
  832.     begin
  833.  
  834. { lock the WE record }
  835.         saveWELock := _WESetHandleLock(hWE, true);
  836.         pWE := hWE^;
  837.         offset := pWE^.selStart;
  838.  
  839. { delete current selection }
  840.         err := _WEDeleteRange(offset, pWE^.selEnd, hWE);
  841.         if (err <> noErr) then
  842.             goto 1;
  843.  
  844. { insert the new text at the insertion point }
  845.         err := _WEInsertText(offset, textPtr, textLength, hWE);
  846.         if (err <> noErr) then
  847.             goto 1;
  848.  
  849. { move the insertion point at the end of the inserted text }
  850.         endOffset := offset + textLength;
  851.         pWE^.selStart := endOffset;
  852.         pWE^.selEnd := endOffset;
  853.  
  854.         if (styleScrap <> nil) then
  855.             begin
  856.  
  857. { if a styleScrap was supplied, apply it to the newly inserted text }
  858.                 err := _WEApplyStyleScrap(offset, endOffset, styleScrap, hWE);
  859.                 if (err <> noErr) then
  860.                     goto 1;
  861.             end;
  862.  
  863. { invalid the null style }
  864.         BCLR(pWE^.flags, weFUseNullStyle);
  865.  
  866. { redraw the text }
  867.         err := _WERedraw(offset, endOffset, hWE);
  868.         if (err <> noErr) then
  869.             goto 1;
  870.  
  871. { clear result code }
  872.         err := noErr;
  873.  
  874. 1:
  875. { return result code }
  876.         WEInsert := err;
  877.  
  878. { unlock the WE record }
  879.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  880.  
  881.     end;  { WEInsert }
  882.  
  883.     procedure _WEInsertByte (theByte: SignedByte;
  884.                                     hWE: WEHandle);
  885.         type
  886.             DoubleByte = packed record
  887.                     firstByte: SignedByte;
  888.                     secondByte: SignedByte;
  889.                 end;
  890.         var
  891.             pWE: WEPtr;
  892.             db: DoubleByte;
  893.             charLength: LongInt;
  894.             byteType: Integer;
  895.             saveFont: Integer;
  896.             savePort: GrafPtr;
  897.     begin
  898.         pWE := hWE^;
  899.         charLength := 1;                { assume 1-byte character by default }
  900.         db.firstByte := theByte;
  901.  
  902. { delete current selection, if any }
  903.         IgnoreShort(_WEDeleteRange(pWE^.selStart, pWE^.selEnd, hWE));
  904.         pWE^.selEnd := pWE^.selStart;
  905.  
  906. { make sure the font script is synchronized with the keyboard script }
  907.         _WESynchNullStyle(hWE);
  908.  
  909.         if BTST(pWE^.flags, weFDoubleByte) then
  910.             begin
  911.  
  912. { special processing for double-byte characters }
  913.                 if (pWE^.firstByte <> 0) then
  914.                     begin
  915.  
  916. { if this byte is the second half of a double-byte character, }
  917. { insert the two bytes at the same time (flush the double-byte cache) }
  918.                         db.firstByte := pWE^.firstByte;
  919.                         db.secondByte := theByte;
  920.                         charLength := 2;
  921.                         pWE^.firstByte := 0;
  922.                     end
  923.                 else
  924.                     begin
  925.  
  926. { determine the byte-type of theByte; first set up the port and its font }
  927.                         GetPort(savePort);
  928.                         SetPort(pWE^.port);
  929.                         saveFont := pWE^.port^.txFont;
  930.                         TextFont(pWE^.nullStyle.tsFont);
  931.  
  932. { call CharByte }
  933.                         byteType := CharByte(@theByte, 0);
  934.  
  935. { put back font and port }
  936.                         TextFont(saveFont);
  937.                         SetPort(savePort);
  938.  
  939. { if theByte is the first half of a double-byte character, just cache it and exit }
  940.                         if (byteType = smFirstByte) then
  941.                             begin
  942.                                 pWE^.firstByte := theByte;
  943.                                 Exit(_WEInsertByte);
  944.                             end;
  945.                     end;
  946.  
  947.             end;  { if double-byte script installed }
  948.  
  949. { insert the new character into the text }
  950.         IgnoreShort(WEInsert(@db, charLength, nil, hWE));
  951.  
  952.     end;  { _WEInsertByte }
  953.  
  954.     procedure _WEBackspace (hWE: WEHandle);
  955.  
  956. { this routine is called by WEKey to handle the backspace key }
  957. { the WE record is guaranteed to be already locked }
  958.  
  959.         var
  960.             pWE: WEPtr;
  961.             offset, charLength: LongInt;
  962.             err: OSErr;
  963.     begin
  964.         pWE := hWE^;
  965.         offset := pWE^.selStart;
  966.  
  967. { if the selection range is not empty, delete the current selection range }
  968.         if (offset < pWE^.selEnd) then
  969.             err := _WEDeleteRange(offset, pWE^.selEnd, hWE)
  970.         else
  971.             begin
  972.  
  973. { do nothing if insertion point is at the beginning of the text }
  974.                 if (offset <= 0) then
  975.                     Exit(_WEBackspace);
  976.  
  977. { determine the byte-type of the character preceding the insertion point }
  978.                 if (WECharByte(offset - 1, hWE) = smSingleByte) then
  979.                     charLength := 1
  980.                 else
  981.                     charLength := 2;
  982.  
  983. { delete the character }
  984.                 offset := offset - charLength;
  985.                 err := _WEDeleteRange(offset, offset + charLength, hWE);
  986.             end;
  987.  
  988. { keep track of current selection range }
  989.         pWE^.selStart := offset;
  990.         pWE^.selEnd := offset;
  991.  
  992. { redraw the text }
  993.         err := _WERedraw(offset, offset, hWE);
  994.  
  995.     end;  { _WEBackspace }
  996.  
  997.     procedure WEKey (key: Char;
  998.                                     modifiers: Integer;
  999.                                     hWE: WEHandle);
  1000.         var
  1001.             pWE: WEPtr;
  1002.             saveWELock: Boolean;
  1003.     begin
  1004.  
  1005. { lock the WE record }
  1006.         saveWELock := _WESetHandleLock(hWE, true);
  1007.         pWE := hWE^;
  1008.  
  1009. { hide the caret if it's showing }
  1010.         if BTST(pWE^.flags, weFCaretVisible) then
  1011.             _WEDrawCaret(hWE);
  1012.  
  1013. { hide the cursor (it will show again as soon as it's moved) }
  1014.         ObscureCursor;
  1015.  
  1016. { dispatch on key class (arrow keys, printable characters, backspace) }
  1017.         if ((ORD(key) >= kArrowLeft) and (ORD(key) <= kArrowDown)) then
  1018.             _WEDoArrowKey(ORD(key), modifiers, hWE)
  1019.         else if (ORD(key) = kBackspace) then
  1020.             _WEBackspace(hWE)
  1021.         else
  1022.             _WEInsertByte(ORD(key), hWE);
  1023.  
  1024. { unlock the WE record }
  1025.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  1026.  
  1027.     end;  { WEKey }
  1028.  
  1029.     function WECut (hWE: WEHandle): OSErr;
  1030.         var
  1031.             err: OSErr;
  1032.     begin
  1033.         WECut := noErr;
  1034.  
  1035. { Cut is just Copy + Delete }
  1036.         err := WECopy(hWE);
  1037.         if (err <> noErr) then
  1038.             begin
  1039.                 WECut := err;
  1040.                 Exit(WECut);
  1041.             end;
  1042.  
  1043.         err := WEDelete(hWE);
  1044.         if (err <> noErr) then
  1045.             begin
  1046.                 WECut := err;
  1047.                 Exit(WECut);
  1048.             end;
  1049.     end;  { WECut }
  1050.  
  1051.     function WESetStyle (mode: Integer;
  1052.                                     var ts: WETextStyle;
  1053.                                     hWE: WEHandle): OSErr;
  1054.         label
  1055.             1;
  1056.         var
  1057.             pWE: WEPtr;
  1058.             fontScript: ScriptCode;
  1059.             saveWELock: Boolean;
  1060.             err: OSErr;
  1061.     begin
  1062.  
  1063. { lock the WE record }
  1064.         saveWELock := _WESetHandleLock(hWE, true);
  1065.         pWE := hWE^;
  1066.  
  1067. { if the selection range is empty, set the null style }
  1068.         if (pWE^.selStart = pWE^.selEnd) then
  1069.             begin
  1070.  
  1071. { first make sure the nullStyle field contains valid information }
  1072.                 _WESynchNullStyle(hWE);
  1073.  
  1074. { apply style changes to the nullStyle record }
  1075.                 _WECopyStyle(ts, pWE^.nullStyle.runStyle, pWE^.nullStyle.tsFace, mode);
  1076.  
  1077. { if the font was altered, synchronize the keyboard script }
  1078.                 if BTST(pWE^.flags, weFNonRoman) then
  1079.                     if BTST(mode, kModeFont) then
  1080.                         begin
  1081.                             fontScript := Font2Script(pWE^.nullStyle.tsFont);
  1082.                             if (fontScript <> GetEnvirons(smKeyScript)) then
  1083.                                 KeyScript(fontScript);
  1084.                         end;
  1085.  
  1086.             end
  1087.         else
  1088.             begin
  1089.  
  1090. { otherwise set the style of the selection range }
  1091.                 err := _WESetStyleRange(pWE^.selStart, pWE^.selEnd, mode, ts, hWE);
  1092.                 if (err <> noErr) then
  1093.                     goto 1;
  1094.  
  1095. { and redraw the text }
  1096.                 err := _WERedraw(pWE^.selStart, pWE^.selEnd, hWE);
  1097.                 if (err <> noErr) then
  1098.                     goto 1;
  1099.  
  1100.             end;
  1101.  
  1102. { clear the result code }
  1103.         err := noErr;
  1104.  
  1105. 1:
  1106. { unlock the WE record }
  1107.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  1108.  
  1109. { return result code }
  1110.         WESetStyle := err;
  1111.  
  1112.     end;  { WESetStyle }
  1113.  
  1114.     function WEUseStyleScrap (styleScrap: WEStyleScrapHandle;
  1115.                                     hWE: WEHandle): OSErr;
  1116.         label
  1117.             1;
  1118.         var
  1119.             pWE: WEPtr;
  1120.             saveWELock: Boolean;
  1121.             err: OSErr;
  1122.     begin
  1123.  
  1124. { lock the WE record }
  1125.         saveWELock := _WESetHandleLock(hWE, true);
  1126.         pWE := hWE^;
  1127.  
  1128. { apply the style scrap to the selection range }
  1129.         err := _WEApplyStyleScrap(pWE^.selStart, pWE^.selEnd, styleScrap, hWE);
  1130.         if (err <> noErr) then
  1131.             goto 1;
  1132.  
  1133. { redraw the text }
  1134.         err := _WERedraw(pWE^.selStart, pWE^.selEnd, hWE);
  1135.  
  1136. 1:
  1137. { return result code }
  1138.         WEUseStyleScrap := err;
  1139.  
  1140. { unlock the WE record }
  1141.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  1142.  
  1143.     end;  { WEUseStyleScrap }
  1144.  
  1145.     function WEPaste (hWE: WEHandle): OSErr;
  1146.         label
  1147.             1;
  1148.         var
  1149.             hText, hStyles: Handle;
  1150.             scrapResult, scrapOffset: LongInt;
  1151.             err: OSErr;
  1152.     begin
  1153.         hText := nil;
  1154.         hStyles := nil;
  1155.  
  1156. { allocate a handle to hold the text }
  1157.         err := _WEAllocate(0, kAllocTemp, hText);
  1158.         if (err <> noErr) then
  1159.             goto 1;
  1160.  
  1161. { look for a 'TEXT' item }
  1162.         scrapResult := GetScrap(hText, kTypeText, scrapOffset);
  1163.         if (scrapResult <= 0) then
  1164.             begin
  1165.                 err := scrapResult;
  1166.                 goto 1;
  1167.             end;
  1168.  
  1169. { allocate a handle to hold the style scrap }
  1170.         err := _WEAllocate(0, kAllocTemp, hStyles);
  1171.         if (err <> noErr) then
  1172.             goto 1;
  1173.  
  1174. { look for a 'styl' item accompanying the text }
  1175.         scrapResult := GetScrap(hStyles, kTypeStyles, scrapOffset);
  1176.  
  1177. { forget the handle if nothing was found or an error occurred }
  1178.         if (scrapResult <= 0) then
  1179.             _WEForgetHandle(hStyles);
  1180.  
  1181. { lock down the text }
  1182.         HLock(hText);
  1183.  
  1184. { insert the text }
  1185.         err := WEInsert(hText^, %_GetHandleSize(hText), WEStyleScrapHandle(hStyles), hWE);
  1186.  
  1187. 1:
  1188. { return result code }
  1189.         WEPaste := err;
  1190.  
  1191. { clean up }
  1192.         _WEForgetHandle(hText);
  1193.         _WEForgetHandle(hStyles);
  1194.  
  1195.     end;  { WEPaste }
  1196.  
  1197. end.